home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / CUGUK / COMMS / C011.ZIP / KERMITPC.C < prev    next >
Text File  |  1990-01-19  |  32KB  |  986 lines

  1. /********************************************************************
  2.  * C Users Group (U.K) C Source Code Library File CUGLIB.011        *
  3.  * Inquiries to: M. Houston, 36 Whetstone Clo. Farquhar Rd.         *
  4.  * Edgbaston, Birmingham B15 2QN ENGLAND                *
  5.  ********************************************************************
  6.  * File name: kermitpc.c
  7.  * Program name: kermitpc
  8.  * Source of file:
  9.  * Purpose: Kermit file transfer program
  10.  * Changes: <who what when & why major changes have been made>      
  11.  ********************************************************************/
  12.  
  13. /*
  14.  *    K e r m i t  File Transfer Utility
  15.  *
  16.  *    UNIX Kermit, Columbia University, 1981, 1982, 1983
  17.  *    Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz
  18.  *
  19.  *    usage: kermit [csr][dlbe line baud escapechar] [f1 f2 ...]
  20.  *
  21.  *    where c=connect, s=send [files], r=receive, d=debug,
  22.  *    l=tty line, b=baud rate, e=escape char (decimal ascii code).
  23.  *    For "host" mode Kermit, format is either "kermit r" to
  24.  *    receive files, or "kermit s f1 f2 ..." to send f1 .. fn.
  25.  *
  26.  *
  27.  *    Fixed up again for Unix, Jim Guyton 7/13/83 (Rand Corp)
  28.  */
  29.  
  30. #include <stdio.h>            /* Standard UNIX definitions */
  31. #include <sgtty.h>
  32. #include <signal.h>
  33. #include <setjmp.h>
  34.  
  35. /* Conditional Compilation: 0 means don't compile it, nonzero means do */
  36.  
  37. #define UNIX        1    /* Conditional compilation for UNIX */
  38. #define TOPS_20     0    /* Conditional compilation for TOPS-20 */
  39. #define VAX_VMS     0    /* Ditto for VAX/VMS */
  40. #define IBM_UTS     0    /* Ditto for Amdahl UTS on IBM systems */
  41.  
  42.  
  43. /* Symbol Definitions */
  44.  
  45. #define MAXPACK     94    /* Maximum packet size */
  46. #define SOH        1    /* Start of header */
  47. #define SP        32    /* ASCII space */
  48. #define CR        015    /* ASCII Carriage Return */
  49. #define DEL        127    /* Delete (rubout) */
  50. #define CTRLD        4
  51. #define BRKCHR        CTRLD    /* Default escape character for CONNECT */
  52.  
  53. #define MAXTRY        5    /* Times to retry a packet */
  54. #define MYQUOTE     '#'     /* Quote character I will use */
  55. #define MYPAD        0    /* Number of padding characters I will need */
  56. #define MYPCHAR     0    /* Padding character I need */
  57. #define MYEOL        '\n'    /* End-Of-Line character I need */
  58. #define MYTIME        5    /* Seconds after which I should be timed out */
  59. #define MAXTIM        20    /* Maximum timeout interval */
  60. #define MINTIM        2    /* Minumum timeout interval */
  61.  
  62. #define TRUE        -1    /* Boolean constants */
  63. #define FALSE        0
  64.  
  65.  
  66. /* Global Variables */
  67.  
  68. int    size,            /* Size of present data */
  69.     n,            /* Message number */
  70.     rpsiz,            /* Maximum receive packet size */
  71.     spsiz,            /* Maximum send packet size */
  72.     pad,            /* How much padding to send */
  73.     timint,         /* Timeout for foreign host on sends */
  74.     numtry,         /* Times this packet retried */
  75.     oldtry,         /* Times previous packet retried */
  76.     fd,            /* File pointer of file to read/write */
  77.     remfd,            /* File pointer of the host's tty */
  78.     image,            /* -1 means 8-bit mode */
  79.     remspd,         /* Speed of this tty */
  80.     host,            /* -1 means we're a host-mode kermit */
  81.     debug;            /* -1 means debugging */
  82.  
  83. char    state,            /* Present state of the automaton */
  84.     padchar,        /* Padding character to send */
  85.     eol,            /* End-Of-Line character to send */
  86.     escchr,         /* Connect command escape character */
  87.     quote,            /* Quote character in incoming data */
  88.     **filelist,        /* List of files to be sent */
  89.     *filnam,        /* Current file name */
  90.     recpkt[MAXPACK],    /* Receive packet buffer */
  91.     packet[MAXPACK];    /* Packet buffer */
  92.  
  93. struct sgttyb
  94.     rawmode,        /* Host tty "raw" mode */
  95.     cookedmode,        /* Host tty "normal" mode */
  96.     remttymode;        /* Assigned tty line "raw" mode */
  97.  
  98. jmp_buf env;            /* Environment ptr for timeout longjump */
  99.  
  100.  
  101. /*
  102.  *    m a i n
  103.  *
  104.  *    Main routine - parse command and options, set up the
  105.  *    tty lines, and dispatch to the appropriate routine.
  106.  */
  107.  
  108. main(argc,argv)
  109. int argc;                /* Character pointer for */
  110. char **argv;                /*  command line arguments */
  111. {
  112.   char *remtty,*cp;            /* tty for CONNECT, char pointer */
  113.   int speed, cflg, rflg, sflg;        /* speed of assigned tty, */
  114.                     /* flags for CONNECT, RECEIVE, SEND */
  115.   if (argc < 2) usage();        /* Make sure there's a command line. */
  116.  
  117.   cp = *++argv; argv++; argc -= 2;    /* Set up pointers to args */
  118.  
  119. /* Initialize this side's SEND-INIT parameters */
  120.  
  121.   eol = CR;                /* EOL for outgoing packets */
  122.   quote = MYQUOTE;            /* Standard control-quote char "#" */
  123.   pad = 0;                /* No padding */
  124.   padchar = NULL;            /* Use null if any padding wanted */
  125.  
  126.   speed = cflg = sflg = rflg = 0;    /* Turn off all parse flags */
  127.   remtty = 0;                /* Default is host (remote) mode */
  128.   image = FALSE;            /* Default to 7-bit mode */
  129.   escchr = BRKCHR;            /* Default escape character */
  130.  
  131.   while ((*cp) != NULL)         /* Get a character from the cmd line */
  132.     switch (*cp++)            /* Based on what the character is, */
  133.      {                    /*  do one of the folloing */
  134.       case '-': break;                  /* Ignore dash (UNIX style) */
  135.       case 'c': cflg++; break;          /* C = CONNECT command */
  136.       case 's': sflg++; break;          /* S = SEND command */
  137.       case 'r': rflg++; break;          /* R = RECEIVE command */
  138.       case 'e': if (argc--)             /* E = specify escape char */
  139.           escchr = atoi(*argv++); /*  as ascii decimal number */
  140.         else usage();
  141.         if (debug) fprintf(stderr,"escape char is ascii %d\n",escchr);
  142.         break;
  143.       case 'l': if (argc--)             /* L = specify tty line to use */
  144.           remtty = *argv++;
  145.         else usage();
  146.         if (debug) fprintf(stderr,"line %s\n",remtty);
  147.         break;
  148. #if UNIX                /* This part only for UNIX systems */
  149.       case 'b': if (argc--) speed = atoi(*argv++); /* Set baud rate */
  150.           else usage();
  151.         if (debug) fprintf(stderr,"speed %d\n",speed); break;
  152.  
  153.       case 'i': image = TRUE; break;    /* Image (8-bit) mode */
  154. #endif /* UNIX */
  155.  
  156.       case 'd': debug = TRUE; break;    /* Debug mode */
  157.      }
  158.  
  159. /* Done parsing */
  160.  
  161.   if ((cflg+sflg+rflg) != 1) usage();    /* Only one command allowed */
  162.  
  163.   remfd = 0;                /* Start out as a host (remote) */
  164.   host = TRUE;
  165.  
  166.   if (remtty)                /* If another tty was specified, */
  167.    {
  168.     remfd = open(remtty,2);        /*  open it */
  169.     if (remfd < 0)            /*  check for failure */
  170.      {
  171.       fprintf(stderr,"Kermit: cannot open %s\n",remtty);
  172.       exit(-1);             /*  Failed, quit. */
  173.      }
  174.     host = FALSE;            /* Opened OK, flag local (not host) */
  175.    }
  176.  
  177. /* Put the tty(s) into the correct modes */
  178.  
  179.   gtty(0,&cookedmode);            /* Save current mode for later */
  180.   gtty(0,&rawmode);
  181.   rawmode.sg_flags |= (RAW|TANDEM);
  182.   rawmode.sg_flags &= ~(ECHO|CRMOD);
  183.  
  184.   gtty(remfd,&remttymode);        /* If local kermit, get mode of */
  185.                     /*  assigned tty */
  186.   remttymode.sg_flags |= (RAW|TANDEM);
  187.   remttymode.sg_flags &= ~(ECHO|CRMOD);
  188.  
  189. #if UNIX                /* Speed changing for UNIX only */
  190.   if (speed)                /* User specified a speed? */
  191.    {
  192.     switch(speed)            /* Get internal system code */
  193.      {
  194.     case 110: speed = B110; break;
  195.     case 150: speed = B150; break;
  196.     case 300: speed = B300; break;
  197.     case 1200: speed = B1200; break;
  198.     case 2400: speed = B2400; break;
  199.     case 4800: speed = B4800; break;
  200.     case 9600: speed = B9600; break;
  201.     default: fprintf(stderr,"bad line speed\n");
  202.      }
  203.     remttymode.sg_ispeed = speed;
  204.     remttymode.sg_ospeed = speed;
  205.    }
  206. #endif /* UNIX */
  207.  
  208.   if (remfd) stty(remfd,&remttymode);    /* Put asg'd tty in raw mode */
  209.  
  210.  
  211. /* All set up, now execute the command that was given. */
  212.  
  213.   if (cflg) connect();            /* CONNECT command */
  214.  
  215.   if (sflg)                /* SEND command */
  216.    {
  217.     if (argc--) filnam = *argv++;    /* Get file to send */
  218.       else usage();
  219.     filelist = argv;
  220.     if (host) stty(0,&rawmode);     /* Put tty in raw mode if remote */
  221.     if (sendsw() == FALSE)        /* Send the file(s) */
  222.       printf("Send failed.\n");         /* Report failure */
  223.     else                /*  or */
  224.       printf("OK\n");                   /* success */
  225.     if (host) stty(0,&cookedmode);    /* Restore tty */
  226.    }
  227.  
  228.   if (rflg)                /* RECEIVE command */
  229.    {
  230.     if (host) stty(0,&rawmode);     /* Put tty in raw mode if remote */
  231.     if (recsw() == FALSE)        /* Receive the file */
  232.       printf("Receive failed.\n");      /* Report failure */
  233.     else                /*  or */
  234.       printf("OK\n");                   /* success */
  235.     if (host) stty(0,&cookedmode);    /* Restore tty */
  236.    }
  237.  }
  238.  
  239. usage()                 /* Give message if user makes */
  240. {                    /* a mistake in the command */
  241.   fprintf(stderr,
  242.      "usage: kermit [csr][di][lbe] [line] [baud] [esc char] [f1 f2 ...]\n");
  243.   exit();
  244. }
  245.  
  246. /*
  247.  *    s e n d s w
  248.  *
  249.  *    Sendsw is the state table switcher for sending
  250.  *    files.    It loops until either it finishes, or
  251.  *    an error is encountered.  The routines called by
  252.  *    sendsw are responsible for changing the state.
  253.  *
  254.  */
  255.  
  256. sendsw()
  257. {
  258.  char sinit(),sfile(),seof(),sdata(),sbreak();
  259.  
  260.  state = 'S';                           /* Send initiate is the start state */
  261.  n = 0;                 /* Initialize message number */
  262.  numtry = 0;                /* Say no tries yet */
  263.  while(TRUE)                /* Do this as long as necessary */
  264.   {
  265.    switch(state)
  266.     {
  267.      case 'D':  state = sdata();  break; /* Data-Send state */
  268.      case 'F':  state = sfile();  break; /* File-Send */
  269.      case 'Z':  state = seof();   break; /* End-of-File */
  270.      case 'S':  state = sinit();  break; /* Send-Init */
  271.      case 'B':  state = sbreak(); break; /* Break-Send */
  272.      case 'C':  return (TRUE);           /* Complete */
  273.      case 'A':  return (FALSE);          /* "Abort" */
  274.      default:    return (FALSE);      /* Unknown, fail */
  275.     }
  276.   }
  277. }
  278.  
  279.  
  280. /*
  281.  *    s i n i t
  282.  *
  283.  *    Send Initiate: Send my parameters, get other side's back.
  284.  */
  285.  
  286. char sinit()
  287. {
  288.   int num, len;             /* Packet number, length */
  289.  
  290.   if (debug) fprintf(stderr,"sinit\n");
  291.   if (numtry++ > MAXTRY) return('A');   /* If too many tries, give up */
  292.   spar(packet);             /* Fill up with init info */
  293.   if (debug) fprintf(stderr,"n = %d\n",n);
  294.  
  295. #if UNIX
  296.   flushinput();             /* Flush pending input */
  297. #endif /* UNIX */
  298.  
  299.   spack('S',n,6,packet);                /* Send an S packet */
  300.   switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  301.    {
  302.     case 'N':  return(state);           /* NAK */
  303.  
  304.     case 'Y':                           /* ACK */
  305.       if (n != num) return(state);    /* If wrong ACK, stay in S state */
  306.       rpar(recpkt);            /* Get other side's init info */
  307.       if (eol == 0) eol = '\n';         /* Check and set defaults */
  308.       if (quote == 0) quote = '#';      /* Control-prefix quote */
  309.       numtry = 0;            /* Reset try counter */
  310.       n = (n+1)%64;            /* Bump packet count */
  311.       if (debug) fprintf(stderr,"Opening %s\n",filnam);
  312.       fd = open(filnam,0);        /* Open the file to be sent */
  313.       if (fd < 0) return('A');          /* if bad file descriptor, give up */
  314.       if (!host) printf("Sending %s\n",filnam);
  315.       return('F');                      /* OK, switch state to F */
  316.  
  317.     case FALSE: return(state);        /* Receive failure, stay in S state */
  318.     default: return('A');               /* Anythig else, just "abort" */
  319.    }
  320.  }
  321.  
  322.  
  323. /*
  324.  *    s f i l e
  325.  *
  326.  *    Send File Header.
  327.  */
  328.  
  329. char sfile()
  330. {
  331.   int num, len;             /* Packet number, length */
  332.  
  333.   if (debug) fprintf(stderr,"sfile\n");
  334.  
  335.   if (numtry++ > MAXTRY) return('A');   /* If too many tries, give up */
  336.   for (len=0; filnam[len] != '\0'; len++); /* Add up the length */
  337.  
  338.   spack('F',n,len,filnam);              /* Send an F packet */
  339.   switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  340.    {
  341.     case 'N':                           /* NAK, just stay in this state, */
  342.       if (n != (num=(--num<0)?63:num))    /*  unless NAK for next packet, */
  343.     return(state);            /*  which is just like an ACK */
  344.                     /*  for this packet, fall thru to... */
  345.     case 'Y':                           /* ACK */
  346.       if (n != num) return(state);    /* If wrong ACK, stay in F state */
  347.       numtry = 0;            /* Reset try counter */
  348.       n = (n+1)%64;            /* Bump packet count */
  349.       size = bufill(packet);        /* Get first data from file */
  350.       return('D');                      /* Switch state to D */
  351.  
  352.     case FALSE: return(state);        /* Receive failure, stay in F state */
  353.     default:    return('A');            /* Something esle, just "abort" */
  354.    }
  355. }
  356.  
  357.  
  358. /*
  359.  *    s d a t a
  360.  *
  361.  *    Send File Data
  362.  */
  363.  
  364. char sdata()
  365. {
  366.   int num, len;             /* Packet number, length */
  367.  
  368.   if (numtry++ > MAXTRY) return('A');   /* If too many tries, give up */
  369.   spack('D',n,size,packet);             /* Send a D packet */
  370.  
  371.   switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  372.    {
  373.     case 'N':                           /* NAK, just stay in this state, */
  374.       if (n != (num=(--num<0)?63:num))    /*  unless NAK for next packet, */
  375.     return(state);            /*  which is just like an ACK */
  376.                     /*  for this packet, fall thru to... */
  377.     case 'Y':                           /* ACK */
  378.       if (n != num) return(state);    /* If wrong ACK, fail */
  379.       numtry = 0;            /* Reset try counter */
  380.       n = (n+1)%64;            /* Bump packet count */
  381.       if ((size = bufill(packet)) == EOF) /* Get data from file */
  382.     return('Z');                    /* If EOF set state to that */
  383.       return('D');                      /* Got data, stay in state D */
  384.  
  385.     case FALSE: return(state);        /* Receive failure, stay in D */
  386.     default:    return('A');            /* Anything else, "abort" */
  387.    }
  388. }
  389.  
  390.  
  391. /*
  392.  *    s e o f
  393.  *
  394.  *    Send End-Of-File.
  395.  */
  396.  
  397. char seof()
  398. {
  399.   int num, len;             /* Packet number, length */
  400.   if (debug) fprintf(stderr,"seof\n");
  401.   if (numtry++ > MAXTRY) return('A');   /* If too many tries, "abort" */
  402.   spack('Z',n,0,packet);                /* Send a 'Z' packet */
  403.   if (debug) fprintf(stderr,"seof1 ");
  404.   switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  405.    {
  406.     case 'N':                           /* NAK, fail */
  407.       if (n != (num=(--num<0)?63:num))    /* ...unless for previous packet, */
  408.     return(state);            /*  in which case, fall thru to ... */
  409.     case 'Y':                           /* ACK */
  410.       if (debug) fprintf(stderr,"seof2 ");
  411.       if (n != num) return(state);    /* If wrong ACK, hold out */
  412.       numtry = 0;            /* Reset try counter */
  413.       n = (n+1)%64;            /* and bump packet count */
  414.       if (debug) fprintf(stderr,"closing %s, ",filnam);
  415.       close(fd);            /* Close the input file */
  416.       if (debug) fprintf(stderr,"ok, getting next file\n");
  417.       if (gnxtfl() == FALSE)        /* No more files go? */
  418.     return('B');                    /* if not, break, EOT, all done */
  419.       if (debug) fprintf(stderr,"new file is %s\n",filnam);
  420.       return('F');                      /* More files, switch state to F */
  421.  
  422.     case FALSE: return(state);        /* Receive failure, stay in state Z */
  423.     default:    return('A');            /* Something else, "abort" */
  424.    }
  425. }
  426.  
  427.  
  428. /*
  429.  *    s b r e a k
  430.  *
  431.  *    Send Break (EOT)
  432.  */
  433.  
  434. char sbreak()
  435. {
  436.   int num, len;             /* Packet number, length */
  437.   if (debug) fprintf(stderr,"sbreak\n");
  438.   if (numtry++ > MAXTRY) return('A');   /* If too many tries "abort" */
  439.  
  440.   spack('B',n,0,packet);                /* Send a B packet */
  441.   switch (rpack(&len,&num,recpkt))    /* What was the reply? */
  442.    {
  443.     case 'N':                           /* NAK, fail */
  444.       if (n != (num=(--num<0)?63:num))    /* ...unless for previous packet, */
  445.     return(state);            /*  in which case, fall thru to ... */
  446.  
  447.     case 'Y':                           /* ACK */
  448.       if (n != num) return(state);    /* If wrong ACK, fail */
  449.  
  450.       numtry = 0;            /* Reset try counter */
  451.       n = (n+1)%64;            /* and bump packet count */
  452.       return('C');                      /* switch state to Complete */
  453.  
  454.     case FALSE: return(state);        /* Receive failure, stay in state B */
  455.     default:    return ('A');           /* Other, "abort" */
  456.    }
  457. }
  458.  
  459.  
  460. /*
  461.  *    r e c s w
  462.  *
  463.  *    This is the state table switcher for receiving files.
  464.  */
  465.  
  466. recsw()
  467. {
  468.   char rinit(),rdata(),rfile();     /* Use these procedures */
  469.  
  470.   state = 'R';                          /* Receive is the start state */
  471.   n = 0;                /* Initialize message number */
  472.   numtry = 0;                /* Say no tries yet */
  473.  
  474.   while(TRUE) switch(state)        /* Do until done */
  475.    {
  476.     case 'D':   state = rdata(); break; /* Data receive state */
  477.     case 'F':   state = rfile(); break; /* File receive state */
  478.     case 'R':   state = rinit(); break; /* Send initiate state */
  479.     case 'C':   return(TRUE);           /* Complete state */
  480.     case 'A':   return(FALSE);          /* "Abort" state */
  481.     }
  482.   }
  483.  
  484. /*
  485.  *    r i n i t
  486.  *
  487.  *    Receive Initialization
  488.  */
  489.  
  490. char rinit()
  491. {
  492.   int len, num;             /* Packet length, number */
  493.  
  494.   if (numtry++ > MAXTRY) return('A');   /* If too many tries, "abort" */
  495.   switch(rpack(&len,&num,packet))    /* Get a packet */
  496.   {
  497.    case 'S':                            /* Send-Init */
  498.      rpar(packet);            /* Get the other side's init data */
  499.      spar(packet);            /* Fill up packet with my init info */
  500.      spack('Y',n,6,packet);             /* ACK with my parameters */
  501.      oldtry = numtry;            /* Save old try count */
  502.      numtry = 0;            /* Start a new counter */
  503.      n = (n+1)%64;            /* Bump packet number, mod 64 */
  504.      return('F');                       /* Enter File-Send state */
  505.  
  506.    case FALSE:    return (state);     /* Didn't get a packet, keep waiting */
  507.    default:    return('A');            /* Some other packet type, "abort" */
  508.   }
  509. }
  510.  
  511.  
  512. /*
  513.  *    r f i l e
  514.  *
  515.  *    Receive File Header
  516.  */
  517.  
  518. char rfile()
  519. {
  520.  int num, len;                /* Packet number, length */
  521.  
  522.  if (numtry++ > MAXTRY) return('A');    /* "abort" if too many tries */
  523.  switch(rpack(&len,&num,packet))    /* Get a packet */
  524.   {
  525.    case 'S':                            /* Send-Init, maybe our ACK lost */
  526.      if (oldtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
  527.      if (num == ((n==0)?63:n-1))    /* Previous packet, mod 64? */
  528.       {                 /* Yes, ACK it again */
  529.        spar(packet);            /* with our Send-Init parameters */
  530.        spack('Y',num,6,packet);         /*  ... */
  531.        numtry = 0;            /* Reset try counter */
  532.        return(state);            /* Stay in this state */
  533.       }
  534.      else return('A');                  /* Not previous packet, "abort" */
  535.  
  536.    case 'Z':                            /* End-Of-File */
  537.      if (oldtry++ > MAXTRY) return('A');
  538.      if (num == ((n==0)?63:n-1))    /* Previous packet, mod 64? */
  539.       {                 /* Yes, ACK it again. */
  540.        spack('Y',num,0,0);
  541.        numtry = 0;
  542.        return(state);            /* Stay in this state */
  543.       }
  544.      else return('A');                  /* Not previous packet, "abort" */
  545.  
  546.    case 'F':                            /* File Header, */
  547.      if (num != n) return('A');         /* which is what we really want */
  548.                     /* The packet number must be right */
  549.      if (!getfil(packet))        /* Try to open a new file */
  550.       {
  551.        fprintf(stderr,"Could not create %s\n"); /* Give up if can't */
  552.        return('A');
  553.       }
  554.      else                /* OK, give message */
  555.        if (!host) printf("Receiving %s\n",packet);
  556.      spack('Y',n,0,0);                  /* Acknowledge the file header */
  557.      oldtry = numtry;            /* Reset try counters */
  558.      numtry = 0;            /* ... */
  559.      n = (n+1)%64;            /* Bump packet number, mod 64 */
  560.      return('D');                       /* Switch to Data state */
  561.  
  562.    case 'B':                            /* Break transmission (EOT) */
  563.      if (num != n) return ('A');        /* Need right packet number here */
  564.      spack('Y',n,0,0);                  /* Say OK */
  565.      return('C');                       /* Go to complete state */
  566.  
  567.    case FALSE: return(state);        /* Couldn't get packet, keep trying */
  568.    default:    return ('A');           /* Some other packet, "abort" */
  569.   }
  570. }
  571.  
  572.  
  573. /*
  574.  *    r d a t a
  575.  *
  576.  *    Receive Data
  577.  */
  578.  
  579. char rdata()
  580. {
  581.  int num, len;                /* Packet number, length */
  582.  
  583.  if (numtry++ > MAXTRY) return('A');    /* "abort" if too many tries */
  584.  switch(rpack(&len,&num,packet))    /* Get packet */
  585.   {
  586.    case 'D':                            /* Got Data packet */
  587.      if (num != n)            /* Right packet? */
  588.       {                 /* No */
  589.        if (oldtry++ > MAXTRY) return('A'); /* If too many tries, give up */
  590.        if (num == ((n==0)?63:n-1))    /* Else check packet number */
  591.     {                /* Previous packet again? */
  592.      spack('Y',num,6,packet);       /* Yes, re-ACK it */
  593.      numtry = 0;            /* Reset try counter */
  594.      return(state);         /* Stay in D, don't write out data! */
  595.     }
  596.        else return('A');                /* sorry wrong number */
  597.       }
  598.                     /* Got data with right packet number */
  599.      bufemp(packet,fd,len);        /* Write the data to the file */
  600.      spack('Y',n,0,0);                  /* Acknowledge the packet */
  601.      oldtry = numtry;            /* Reset the try counters */
  602.      numtry = 0;            /* ... */
  603.      n = (n+1)%64;            /* Bump packet number, mod 64 */
  604.      return('D');                       /* Remain in data state */
  605.  
  606.    case 'F':                            /* Got a File Header */
  607.      if (oldtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
  608.      if (num == ((n==0)?63:n-1))    /* Else check packet number */
  609.       {                 /* It was the previous one */
  610.        spack('Y',num,0,0);              /* ACK it again */
  611.        numtry = 0;            /* Reset try counter */
  612.        return(state);            /* Stay in Data state */
  613.       }
  614.      else return('A');                  /* Not previous packet, "abort" */
  615.  
  616.    case 'Z':                            /* End-Of-File */
  617.      if (num != n) return('A');         /* Must have right packet number */
  618.      spack('Y',n,0,0);                  /* OK, ACK it. */
  619.      close(fd);             /* Close the file */
  620.      n = (n+1)%64;            /* Bump packet number */
  621.      return('F');                       /* Go back to Receive File state */
  622.  
  623.    case FALSE:    return(state);        /* No packet came, keep waiting */
  624.    default:    return('A');            /* Some other packet, "abort" */
  625.   }
  626. }
  627.  
  628. /*
  629.  *    c o n n e c t
  630.  *
  631.  *    Establish a virtual terminal connection with the remote host, over an
  632.  *    assigned tty line.
  633.  *
  634.  */
  635.  
  636. connect()
  637. {
  638.   int parent;                /* Fork handle */
  639.   char c = NULL, r = '\r';
  640.  
  641.   if (host)        /* If in host mode, nothing to connect to */
  642.    {
  643.     fprintf(stderr,"Kermit: nothing to connect to\n");
  644.     return;
  645.    }
  646.  
  647.   parent = fork();    /* Start fork to get typeout from remote host */
  648.  
  649.   if (parent)        /* Parent passes typein to remote host */
  650.    {
  651.     printf("Kermit: connected.\r\n");   /* Give message */
  652.     stty(0,&rawmode);            /* Put tty in raw mode */
  653.     read(0,&c,1);            /* Get a character */
  654.     while ((c&0177) != escchr)        /* Check for escape character */
  655.      {                    /* Not it */
  656.       write(remfd,&c,1);        /* Write the character on screen */
  657.       c = NULL;             /* Nullify it */
  658.       read(0,&c,1);            /* Get next character */
  659.      }                    /* Until escape character typed */
  660.     kill(parent,9);            /* Done, get rid of fork */
  661.     stty(0,&cookedmode);        /* Restore tty mode */
  662.     printf("\nKermit: disconnected.\n"); /* Give message */
  663.     return;                /* Done */
  664.    }
  665.   else            /* Child does the reading from the remote host */
  666.    {
  667.     while(1)                /* Do this forever */
  668.      {
  669.       read(remfd,&c,1);
  670.       write(1,&c,1);
  671.      }
  672.    }
  673. }
  674.  
  675. /*
  676.  *    KERMIT utilities.
  677.  */
  678.  
  679. clkint()                /* Timer interrupt handler */
  680. {
  681.  longjmp(env,TRUE);            /* Tell rpack to give up */
  682. }
  683.  
  684. /* tochar converts a control character to a printable one by adding a space */
  685.  
  686. char tochar(ch)
  687. char ch;
  688. {
  689.   return(ch + ' ');             /* make sure not a control char */
  690. }
  691.  
  692.  
  693. /* unchar undoes tochar */
  694.  
  695. char unchar(ch)
  696. char ch;
  697. {
  698.   return(ch - ' ');             /* restore char */
  699. }
  700.  
  701.  
  702. /*
  703.  * ctl turns a control character into a printable character by toggling the
  704.  * control bit (ie. ^A becomes A and A becomes ^A).
  705.  */
  706.  
  707. char ctl(ch)
  708. char ch;
  709. {
  710.  return(ch ^ 64);        /* toggle the control bit */
  711. }
  712.  
  713.  
  714. /*
  715.  *    s p a c k
  716.  *
  717.  *    Send a Packet
  718.  */
  719.  
  720. spack(type,num,len,data)
  721. char type, *data;
  722. int num, len;
  723. {
  724.   int i;                /* Character loop counter */
  725.   char chksum, buffer[100];        /* Checksum, packet buffer */
  726.   register char *bufp;            /* Buffer pointer */
  727.  
  728.   bufp = buffer;            /* Set up buffer pointer */
  729.   for (i=1; i<=pad; i++) write(remfd,&padchar,1); /* Issue any padding */
  730.  
  731.   *bufp++ = SOH;            /* Packet marker, ASCII 1 (SOH) */
  732.   chksum = tochar(len+3);        /* Initialize the checksum */
  733.   *bufp++ = tochar(len+3);        /* Send the character count */
  734.   chksum = chksum + tochar(num);    /* Init checksum */
  735.   *bufp++ = tochar(num);        /* Packet number */
  736.   chksum = chksum + type;        /* Accumulate checksum */
  737.   *bufp++ = type;            /* Packet type */
  738.  
  739.   for (i=0; i<len; i++)         /* Loop for all data characters */
  740.    {
  741.     *bufp++ = data[i];            /* Get a character */
  742.     chksum = chksum+data[i];        /* Accumulate checksum */
  743.    }
  744.   chksum = (chksum + (chksum & 192) / 64) & 63; /* Compute final checksum */
  745.   *bufp++ = tochar(chksum);        /* Put it in the packet */
  746.   *bufp = eol;                /* Extra-packet line terminator */
  747.   write(remfd, buffer,bufp-buffer+1);    /* Send the packet */
  748.   if (debug) putc('.',stderr);
  749. }
  750.  
  751. /*
  752.  *  r p a c k
  753.  *
  754.  *  Read a Packet
  755.  *
  756.  */
  757.  
  758. rpack(len,num,data)
  759. int *len, *num;             /* Packet length, number */
  760. char *data;                /* Packet data */
  761. {
  762.  int i, done;                /* Data character number, loop exit */
  763.  char chksum, t, type;            /* Checksum, current char, pkt type */
  764.  
  765. #if UNIX                /* TOPS-20 can't handle timeouts... */
  766.  if (setjmp(env)) return FALSE;     /* Timed out, fail */
  767.  signal(SIGALRM,clkint);        /* Setup the timeout */
  768.  if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME;
  769.  alarm(timint);
  770.  
  771.  flushinput();                /* Flush any pending input */
  772. #endif /* UNIX */
  773.  
  774.  while (t != SOH)            /* Wait for packet header */
  775.   {
  776.    read(remfd,&t,1);
  777.    if (!image) t &= 0177;        /* Handle parity */
  778.   }
  779.  
  780.  done = FALSE;                /* Got SOH, init loop */
  781.  while (!done)                /* Loop to get a packet */
  782.   {
  783.    read(remfd,&t,1);            /* Get character */
  784.    if (!image) t &= 0177;        /* Handle parity */
  785.    if (t == SOH) continue;        /* Resynchronize if SOH */
  786.  
  787.    chksum = t;                /* Start the checksum */
  788.    *len = unchar(t)-3;            /* Character count */
  789.  
  790.    read(remfd,&t,1);            /* Get character */
  791.    if (!image) t &= 0177;        /* Handle parity */
  792.    if (t == SOH) continue;        /* Resynchronize if SOH */
  793.    chksum = chksum + t;         /* Accumulate checksum */
  794.    *num = unchar(t);            /* Packet number */
  795.  
  796.    read(remfd,&t,1);            /* Get character */
  797.    if (!image) t &= 0177;        /* Handle parity */
  798.    if (t == SOH) continue;        /* Resynchronize if SOH */
  799.    chksum = chksum + t;         /* Accumulate checksum */
  800.    type = t;                /* Packet type */
  801.  
  802.    for (i=0; i<*len; i++)        /* The data itself, if any */
  803.     {                    /* Loop for character count */
  804.      read(remfd,&t,1);            /* Get character */
  805.      if (!image) t &= 0177;        /* Handle parity */
  806.      if (t == SOH) continue;        /* Resynch if SOH */
  807.      chksum = chksum + t;        /* Accumulate checksum */
  808.      data[i] = t;            /* Put it in the data buffer */
  809.     }
  810.    data[*len] = 0;            /* Mark the end of the data */
  811.  
  812.    read(remfd,&t,1);            /* Get last character (checksum) */
  813.    if (!image) t &= 0177;        /* Handle parity */
  814.    if (t == SOH) continue;        /* Resynchronize if SOH */
  815.    done = TRUE;             /* Got checksum, done */
  816.   }
  817. #if UNIX
  818.  alarm(0);                /* Disable the timer interrupt */
  819. #endif
  820.  
  821.  chksum = (chksum + (chksum & 192) / 64) & 63; /* Fold bits 7,8 into chksum */
  822.  if (chksum != unchar(t)) return(FALSE); /* Check the checksum, fail if bad */
  823.  
  824.  return(type);                /* All OK, return packet type */
  825. }
  826.  
  827.  
  828.  
  829. /*
  830.  *    b u f i l l
  831.  *
  832.  *    Get a bufferful of data from the file that's being sent.
  833.  *    Only control-quoting is done; 8-bit & repeat count prefixes are
  834.  *    not handled.
  835.  */
  836.  
  837. bufill(buffer)
  838. char buffer[];                /* Buffer */
  839.  {
  840.   int i;                /* Loop index */
  841.   char t,t7;
  842.   i = 0;                /* Init data buffer pointer */
  843.   while(read(fd,&t,1) > 0)        /* Get the next character */
  844.    {
  845.     if (image)                /* In 8-bit mode? */
  846.      {
  847.       t7 = t & 0177;            /* Yes, look at low-order 7 bits */
  848.       if (t7 < SP || t7==DEL || t7==quote) /* Control character? */
  849.        {                /*    Yes, */
  850.     buffer[i++] = quote;        /*    quote this character */
  851.     if (t7 != quote) t = ctl(t);    /* and uncontrollify */
  852.        }
  853.      }
  854.     else                /* Else, ASCII text mode */
  855.      {
  856.       t &= 0177;            /* Strip off the parity bit */
  857.       if (t < SP || t == DEL || t == quote) /* Control character? */
  858.        {                /* Yes */
  859.     if (t=='\n')                    /* If newline, squeeze CR in first */
  860.      {
  861.       buffer[i++] = quote;
  862.       buffer[i++] = ctl('\r');
  863.      }
  864.     buffer[i++] = quote;        /* Insert quote */
  865.     if (t != quote) t=ctl(t);    /* Uncontrollified the character */
  866.        }
  867.      }
  868.     buffer[i++] = t;            /* Deposit the character itself */
  869.     if (i >= spsiz-8) return(i);    /* Check length */
  870.    }
  871.   if (i==0) return(EOF);        /* Wind up here only on EOF */
  872.   return(i);                /* Handle partial buffer */
  873. }
  874.  
  875. /*
  876.  *    b u f e m p
  877.  *
  878.  *    Get data from an incoming packet into a file.
  879.  */
  880.  
  881. bufemp(buffer,fd,len)
  882. char buffer[];                /* Buffer */
  883. int fd, len;                /* File pointer, length */
  884.  {
  885.   int i;                /* Counter */
  886.   char t;                /* Character holder */
  887.  
  888.   for (i=0; i<len; i++)         /* Loop thru the data field */
  889.    {
  890.     t = buffer[i];            /* Get character */
  891.     if (t == MYQUOTE)            /* Control quote? */
  892.      {                    /* Yes */
  893.       t = buffer[++i];            /* Get the quoted character */
  894.       if ((t & 0177) != MYQUOTE)    /* Low order bits match quote char? */
  895.     t = ctl(t);            /* No, uncontrollify it */
  896.      }
  897.     if (image || (t != CR))        /* Don't pass CR in text mode */
  898.       write(fd,&t,1);            /* Put the char in the file */
  899.    }
  900.  }
  901.  
  902.  
  903. /*
  904.  *    g e t f i l
  905.  *
  906.  *    Open a new file
  907.  */
  908.  
  909. getfil(filenm)                /* Bizarre logic, as this is called */
  910. char *filenm;                /* only with packet as the arg! */
  911. {
  912.   if (filenm[0] == '\0')
  913.     fd = creat(packet,0644);        /* If filename known, use it */
  914.   else
  915.     fd = creat(filenm,0644);        /* else use sourcefile name */
  916.   return (fd > 0);            /* Return false if file won't open */
  917. }
  918.  
  919. /*
  920.  *    g n x t f l
  921.  *
  922.  *    Get next file in a file group
  923.  */
  924.  
  925. gnxtfl()
  926. {
  927.   if (debug) fprintf(stderr, "gnxtfl\n");
  928.   filnam = *(filelist++);
  929.   if (filnam == 0) return FALSE;    /* If no more, fail */
  930.   else return TRUE;            /* else succeed */
  931. }
  932.  
  933. /*
  934.  *    s p a r
  935.  *
  936.  *    Fill the data array with my send-init parameters
  937.  *
  938.  */
  939.  
  940. spar(data)
  941. char data[];
  942. {
  943.   data[0] = tochar(MAXPACK);        /* Biggest packet I can receive */
  944.   data[1] = tochar(MYTIME);        /* When I want to be timed out */
  945.   data[2] = tochar(MYPAD);        /* How much padding I need */
  946.   data[3] = ctl(MYPCHAR);        /* Padding character I want */
  947.   data[4] = tochar(MYEOL);        /* End-Of-Line character I want */
  948.   data[5] = MYQUOTE;            /* Control-Quote character I send */
  949. }
  950.  
  951.  
  952. /*    r p a r
  953.  *
  954.  *    Get the other host's send-init parameters
  955.  *
  956.  */
  957.  
  958. rpar(data)
  959. char data[];
  960. {
  961.   spsiz = unchar(data[0]);        /* Maximum send packet size */
  962.   timint = unchar(data[1]);        /* When I should time out */
  963.   pad = unchar(data[2]);        /* Number of pads to send */
  964.   padchar = ctl(data[3]);        /* Padding character to send */
  965.   eol = unchar(data[4]);        /* EOL character I must send */
  966.   quote = data[5];            /* Incoming data quote character */
  967. }
  968.  
  969. #if UNIX
  970. flushinput()
  971. {
  972.   long int count;            /* Number of bytes ready to read */
  973.   long int i;                /* Number of bytes to read in loop */
  974.  
  975.   ioctl(remfd, FIONREAD, &count);    /* See how many bytes pending read */
  976.   if (!count) return;            /* If zero, then no input to flush */
  977.  
  978.   while (count) {            /* Loop till all are flushed */
  979.       i = (count<sizeof(recpkt)) ?    /* Read min of count and size of */
  980.            count : sizeof(recpkt);    /*  the read buffer */
  981.       read(remfd, recpkt, i);        /* Read a bunch */
  982.       count -= i;            /* Subtract from amount to read */
  983.   }
  984. }
  985. #endif /* UNIX */
  986.